﻿using CommunicationClass.Auxiliary;
using CommunicationClass.Client.Model;
using CommunicationClass.Model;
using CommunicationClass.Server.Model;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using static CommunicationClass.Client.Model.TCPClientDelegate;
using static CommunicationClass.Model.SendReceiveEnum;

namespace CommunicationClass.Client
{
    public class ClientCommunication
    {

        private Socket TcpSocket = null;
        private byte[] buffer = new byte[MessageSendConfigure.MaxMessageTcpSendM * 1024*1024];
        private byte[] Udpbuffer = new byte[MessageSendConfigure.MaxMessageUdpSendB];
        private string TcpHost ="";
        private string UdpHost = "";
        private int TcpPort = 0;
        private int UdpPort = 0;
        /// <summary>
        /// 创建基于TCP和UDP的通讯实例
        /// </summary>
        /// <param name="TcpHost">服务器通讯地址</param>
        /// <param name="TcpPort">服务器Tcp端口号</param>
        /// <param name="UdpHost">服务器通讯地址</param>
        /// <param name="UdpPort">服务器Udp端口号</param>
        public ClientCommunication(string TcpHost, int TcpPort,string UdpHost, int UdpPort)
        {
            try
            {

                this.TcpHost = TcpHost;
                this.TcpPort = TcpPort;
                this.UdpHost = UdpHost;
                this.UdpPort = UdpPort;
                TcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //建立连接
                TcpSocket.Connect(TcpHost, TcpPort);
                Thread.Sleep(50);
                //实现接受消息的方法
                TcpSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), TcpSocket);



                //建立Udp打洞协议 此方法切往服务器发送打洞数据
                UdpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                UdpSocket.Bind(new IPEndPoint(IPAddress.Any, UdpPort));
                //建立连接
                UdpSocket.Connect(UdpHost,UdpPort);
                //创建接收消息

            }
            catch (Exception ex)
            {

            }
        }

  

        /// <summary>
        /// 登陆回调函数 此方法是异步操作 UI操作需要自行转换
        /// </summary>
        public event DelegateIsSendMessage LoginEvent;
        /// <summary>
        /// 发送消息回调函数 此方法是异步操作，UI操作需要自行转换
        /// </summary>
        public event DelegateIsSendMessage SendMessageEvent;
        /// <summary>
        /// 接收消息回调函数 此方法是异步操作，UI操作需要自行转换
        /// </summary>
        public event DelegateReceiveMessage ReceiveMessageEvent;
        /// <summary>
        /// 未登录事件
        /// </summary>
        public event DelegateIsSendMessage NoLogin;

        /// <summary>
        /// 用户登录加载基本信息 UI操作需自行转换UI线程
        /// </summary>
        public event DelegateLoadData LoadUserData;

        /// <summary>
        /// 打洞建立连接
        /// </summary>
        public event DelegateIsConnect IsVoiceConnect;



        /// <summary>
        /// 异步登录 密码是32位MD5加密
        /// 异常信息：次方法必须设置登录回调LoginEvent
        /// </summary>
        /// <param name="UserID">用户名</param>
        /// <param name="Pwd">密码</param>
        public void Login(string UserID,string Pwd)
        {
            if (LoginEvent == null)
            {
                throw new Exception("次方法必须设置登录回调！");
            }
            try
            {
                if (!TcpSocket.Connected)
                {
                    //关闭之前连接
                    TcpSocket.Close();
                    //建立连接
                    TcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    //建立连接
                    TcpSocket.Connect(TcpHost, TcpPort);
                    Thread.Sleep(50);
                    TcpSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), TcpSocket);
                }

                SendModel sendmodel = new SendModel();
                sendmodel.Data = Encoding.Unicode.GetBytes(UserID + CommunicationClass.Auxiliary.MD5.UserMd5(Pwd) + (int)MsgLoginTypeEnum.客户端);
                sendmodel.SendTypeEnum = SendOrReceiveTypeEnum.登录;

                byte[] outputBuffer = SendLengthCalculation.SendJsonConvert(sendmodel);
                //异步发送消息 需要额外处理并发性
                TcpSocket.BeginSend(outputBuffer, 0, outputBuffer.Length, SocketFlags.None, null, null);
            }
            catch (Exception ex)
            {
                LoginEvent?.BeginInvoke(false, "登录运行错误,详细信息如下：\r\n"+ex.Message,null,null);
            }
        }
        public enum SendTypeEnum
        {
            普通消息= 21,
            图片=15,
            短视频=16,
            语音消息=14
            
        }
        /// <summary>
        /// UDP监听
        /// </summary>
        private Socket UdpSocket;
        /// <summary>
        /// 记录用户socket 打洞信息
        /// </summary>
        private Hashtable UserConnect = new Hashtable();

        /// <summary>
        /// 请求语音通话
        /// </summary>
        /// <param name="ReceiveUserID">通话用户</param>
        /// <param name="ReceiveIntPtr">语音播放句柄</param>
        /// <returns>摧毁会话钥匙</returns>
        public string OpenVoice(string ReceiveUserID, IntPtr ReceiveIntPtr)
        {
            string key = DateTime.Now.ToFileTimeUtc().ToString();
            Socket socket = null;
            //判断是否与用户建立UDP通讯
            lock (UserConnect.SyncRoot)
            {
                if (UserConnect.ContainsKey(ReceiveUserID))
                {
                    socket = UserConnect[ReceiveUserID] as Socket;
                }
            }

            if (socket == null)
            {
                //发送打洞信息
                SendModel sendModel = new SendModel();
                sendModel.SendTypeEnum = SendOrReceiveTypeEnum.用户打洞;
                sendModel.Data =JsonHelper.JsonDateSerializer(new UserConnectHole() { ReceiveUserID = ReceiveUserID, ConnectHoleType= UserConnectHole.ConnectHoleTypeEnum.Voice });
                
                if (!TcpSocket.Connected)
                {
                    //关闭之前连接
                    TcpSocket.Close();
                    //建立连接
                    TcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    //建立连接
                    TcpSocket.Connect(TcpHost, TcpPort);
                    Thread.Sleep(50);
                    TcpSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), TcpSocket);
                }

                byte[] outputBuffer = SendLengthCalculation.SendJsonConvert(sendModel);
                //异步发送消息 需要额外处理并发性
                TcpSocket.BeginSend(outputBuffer, 0, outputBuffer.Length, SocketFlags.None, null, null);
                //监听是否打洞
                //启动一个线程监听
                Thread t = new Thread(VoiceConnect);
                lock (ThreadClose.SyncRoot)
                {
                    ThreadClose.Add(key,t);
                }
                t.Start(new object[] { ReceiveUserID, ReceiveIntPtr } );
            }
            else
            {
                //向用户发送语音通话
            }

            return key;
        }
        private void VoiceConnect(object values)
        {
            string ReceiveUserID = (values as object[])[0].ToString();
            IntPtr ReceiveIntPtr = (IntPtr)(values as object[])[1];
            //等待60对方没有接受将取消连接
            for (int i = 0; i < 60; i++)
            {
                //等待1秒,判断用户是否建立连接
                Thread.SpinWait(1000);
                if (UserConnect.ContainsKey(ReceiveUserID))
                {
                    //连接建立成功！
                    //建立语音消息
                    SocketVoiceHelper socketVoiceHelper = new SocketVoiceHelper();
                    socketVoiceHelper.LocalSocket = UserConnect[ReceiveUserID] as Socket;
                    socketVoiceHelper.Intptr = ReceiveIntPtr;
                    socketVoiceHelper.NotifyNum = 1;
                    //socketVoiceHelper.NotifySize = 44100;
                    socketVoiceHelper.StartVoiceCapture();
                    break;
                }

            }

        }
        /// <summary>
        /// 建立打洞监听集合
        /// </summary>
        private Hashtable ThreadClose = new Hashtable();
        /// <summary>
        /// 摧毁语音通话
        /// </summary>
        /// <returns></returns>
        public bool CloseVoice(string CloseKey)
        {
            try
            {
                if (ThreadClose.ContainsKey(CloseKey))
                {

                    Thread t = ThreadClose[CloseKey] as Thread;
                    t.Abort();
                    return true;

                }
            }
            catch (Exception ex)
            {

                
            }
         
            return false;
        }
        /// <summary>
        /// 发送消息
        /// 语音消息,小视频消息,图片,数据放入：MsgOtherContent,字段。
        /// 如果是语音消息,小视频消息,消息内容我们会在视频音频或语音消息中提取(未编写云库，暂时不转换！)。
        /// </summary>
        /// <param name="messageModel">消息内容</param>
        /// <param name="SendType">消息类型</param>
        public bool SendMessage(MessageModel messageModel, SendTypeEnum SendType)
        {
            //发送消息
            try
            {
                if (!TcpSocket.Connected)
                {
                    //关闭之前连接
                    TcpSocket.Close();
                    //建立连接
                    TcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    //建立连接
                    TcpSocket.Connect(TcpHost, TcpPort);
                    Thread.Sleep(50);
                    TcpSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), TcpSocket);
                }
                SendModel sendmodel = new SendModel();
                if (SendType == SendTypeEnum.语音消息)
                {
                    //将语音转换文字,未编写云识别库和算法程序，暂时未对接
                    //发送打洞连接

                }
                else if (SendType == SendTypeEnum.短视频)
                {
                    //将视频转换成语音,然后在转换成文字，未编写云识别库和算法程序，暂时未对接


                }
                else if (SendType == SendTypeEnum.图片)
                {
                    //提取图片内容文本，目前未编写云识别库和算法程序，暂时未对接

                }
                else
                {

                }
                //普通消息
                sendmodel.Data = JsonHelper.JsonDateSerializer<MessageModel>(messageModel);

                sendmodel.SendTypeEnum = (SendOrReceiveTypeEnum)SendType;

                byte[] outputBuffer = SendLengthCalculation.SendJsonConvert(sendmodel);
                //异步发送消息 需要额外处理并发性
                TcpSocket.BeginSend(outputBuffer, 0, outputBuffer.Length, SocketFlags.None, null, null);

            }
            catch (Exception ex)
            {
                LoginEvent?.BeginInvoke(false, "发送消息运行错误,详细信息如下：\r\n" + ex.Message,null,null);
                return false;
            }
            return true;
        }
        
        private void ReceiveMessage(IAsyncResult ar)
        {
            try
            {
                var socket = ar.AsyncState as Socket;
                try
                {
                    var length = socket.EndReceive(ar);
                    List<SendModel> listReceiveModel = ReceiveLengthCalculation.ReceiveMessageCalculation<SendModel>(buffer,length);
                    foreach (SendModel receiveType in listReceiveModel)
                    {
                        if (receiveType.SendTypeEnum == SendOrReceiveTypeEnum.普通消息 ||
                            receiveType.SendTypeEnum == SendOrReceiveTypeEnum.图片 ||
                            receiveType.SendTypeEnum == SendOrReceiveTypeEnum.语音消息
                            || receiveType.SendTypeEnum == SendOrReceiveTypeEnum.短视频)
                        {

                            MessageModel messageModel = Auxiliary.JsonHelper.JsonDateDeserialize<MessageModel>(receiveType.Data);
                            ReceiveMessageEvent?.BeginInvoke(messageModel, (SendTypeEnum)receiveType.SendTypeEnum, null, null);
                        }
                        else
                        {
                            switch (receiveType.SendTypeEnum)
                            {
                                case SendOrReceiveTypeEnum.用户打洞:
                                    {
                                        //用户自定义判断是否建立连接
                                        UserConnectHole userConnect = Auxiliary.JsonHelper.JsonDateDeserialize<UserConnectHole>(receiveType.Data);
                                        //用户是同意还是拒绝连接
                                        if (IsVoiceConnect?.Invoke(userConnect) == true)
                                        {
                                            //UDP的socket
                                            Socket ConnectSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                                            //建立连接
                                            ConnectSocket.Connect(userConnect.SendIp, userConnect.SendPort);
                                            //添加到UDP关联集合
                                            //判断是否存在当前用户的UDP打洞连接
                                            if (!UserConnect.Contains(userConnect.SendUserID))
                                            {
                                                lock (UserConnect.SyncRoot)
                                                {
                                                    UserConnect.Add(userConnect.SendUserID, ConnectSocket);
                                                }
                                            }
                                            else
                                            {
                                                //存在建立连接更新
                                                lock (UserConnect.SyncRoot)
                                                {
                                                    UserConnect[userConnect.SendUserID] = ConnectSocket;
                                                }
                                            }
                                        }
                                        else
                                        {
                                            //对方拒绝打洞
                                        }

                                        //向服务器发送打洞响应数据
                                        //拒绝，同意，未响应

                                        break;
                                    }
                                case SendOrReceiveTypeEnum.登录答应:
                                    {
                                        
                                        ReceiveModel messageModel = Auxiliary.JsonHelper.JsonDateDeserialize<ReceiveModel>(receiveType.Data);
                                        if (messageModel.IsSuccess)
                                        {
                                            string[] data = Auxiliary.JsonHelper.JsonDateDeserializeString<string[]>(messageModel.Data);
                                            LoginEvent?.BeginInvoke(messageModel.IsSuccess, data[0], null, null);
                                            LoadUserData?.BeginInvoke(data[1], null, null);
                                            string UserKey = data[2];//用户唯一标识 用于UDP的建立连接，只能用一次,创建UDP服务器连接用
                                                                     //向服务器发送UDP数据 告诉它本地UDP监听信息

                                            if (UdpSocket==null)
                                            {
                                                //建立连接
                                                UdpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                                                //建立连接
                                                UdpSocket.Connect(UdpHost, UdpPort);
                                                Thread.Sleep(50);
                                                //创建接收消息
                                            }else if (!UdpSocket.Connected)
                                            {
                                                UdpSocket.Connect(UdpHost, UdpPort);
                                            }
                                            byte[] DataSend = JsonHelper.JsonDateSerializer(UserKey);
                                            //与服务器建立连接
                                            EndPoint point = new IPEndPoint(IPAddress.Parse(UdpHost), UdpPort);
                                            UdpSocket.SendTo(DataSend, point);
                                            
                                        }
                                        else
                                        {
                                            LoginEvent?.BeginInvoke(messageModel.IsSuccess, messageModel.Data, null, null);
                                        }
                                        break;
                                    }
                                case SendOrReceiveTypeEnum.普通消息答应:
                                    {
      
                                        ReceiveModel messageModel = Auxiliary.JsonHelper.JsonDateDeserialize<ReceiveModel>(receiveType.Data);
                                        SendMessageEvent?.BeginInvoke(messageModel.IsSuccess, messageModel.Data, null, null);
                                        break;
                                    }
                                case SendOrReceiveTypeEnum.未登录:
                                    {

                                        ReceiveModel messageModel = Auxiliary.JsonHelper.JsonDateDeserialize<ReceiveModel>(receiveType.Data);
                                        NoLogin?.BeginInvoke(messageModel.IsSuccess, messageModel.Data, null, null);
                                        break;
                                    }
                                default:
                                    break;
                            }

                        }



                    }
                   
                }
                catch (Exception ex)
                {

                    LoginEvent?.BeginInvoke(false, "发送消息运行错误,详细信息如下：\r\n" + ex.Message, null, null);
                }

                //接收下一个消息(因为这是一个递归的调用，所以这样就可以一直接收消息了）
                socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
            }
            catch (Exception ex)
            {
                
            }
            


        }

    }
}
